#! /usr/bin/env python
"""
pyorhythms - chart a person's or group's biorhythms
"""
#Calculation from http://en.wikipedia.org/wiki/Biorhythm
#
#The formulae for the curves are
#
#    * physical: sin(2*pi*t / 23),
#    * emotional: sin(2*pi*t / 28) and
#    * intellectual: sin(2*pi*t / 33),
#
#where t indicates the number of days since birth.

from datetime import date, timedelta
import getopt
from math import sin, pi
import sys
import time


#http://matplotlib.sourceforge.net/tutorial.html
#http://matplotlib.sourceforge.net/matplotlib.pylab.html
import pylab

__revision__ = "$Rev: 12 $"[6:-2]  #just the numeric portion of the string

class Person():
    """
    Person Class docstring
    """
    def __init__(self, name, 
                        birthday, 
                        startdate=date.today(), 
                        number_of_days=33):
        """Prep a person object
        
            name is a non-empty string identifying the individual
            birthday is a date object
            startdate is a date object
            number_of_days is an integer specify the length of the chart
            linestyle - is a magic character string that is pylab specific, to 
                specify the type of line to draw for the person
                '-' a continous line
                '-.' a dotted line
                '--' a dashed line
            format - is a magic character the is pylab specific
                'o' - a point
                's' - a square
                '^' - a triangle
                'p' - a pentagon
                ''  - no point markers
            
            """
        self.name = name
        #time.strptime returns a 9 element list, of which we want the first 3 
        # Year.Month.Date pass the list of 3 elements (Y,m,d) as parameters 
        # to the date() function with the * operator
        self.birthday = birthday
        
        enddate = startdate + timedelta(days=number_of_days)
        self.dates = pylab.drange(  startdate, 
                                                    enddate, 
                                                    timedelta(days=1)
                                                )
        self.days = map(self.num_days, self.dates)
        # other drawing styles -->
        #
        # 'r' red line, 'g' green line, 'y' yellow line
        #
        # 'ro' red dots as markers, 'r.' smaller red dots, 'r+' red pluses
        #
        # 'r--' red dashed line, 'g^' green triangles, 'bs' blue squares
        #
        # 'rp' red pentagons, 'r1', 'r2', 'r3', 'r4' well, check out the markers
        #        
        self.linestyle = '-'
        self.fmt = ' '
            
    
    def num_days(self, pl_date):
        """
        num_days(pl_date) -> integer
        
        where d is a pylab date (float) 
        
        Returns the number of days the between the person's birthday and the
        date specified by pl_date
        """
        #since pylab dates are floats, need to convert to a datetime then lop 
        # off the time, return a plain date object to match self.birthDay
        ddif = pylab.num2date(pl_date).date() -  self.birthday
        return ddif.days
        


def physical(dayslived):
    """
    physical(dayslived) -> float 
    
    where dayslived is an integer
    
    returns a float f in the range 1 <= f => -1
    """
    return sin((2 * pi * dayslived)/23)
    
def emotional(dayslived):
    """
    emotional(dayslived) -> float 
    
    where dayslived is an integer
    
    returns a float f in the range 1 <= f => -1
    """
    return sin((2 * pi * dayslived)/28)
    
def intellectual(dayslived):
    """
    intellectual(dayslived) -> float 
    
    where dayslived is an integer
    
    returns a float f in the range 1 <= f => -1
    """
    return sin((2 * pi * dayslived)/33)

def plot(person):
    """
    plot the graph for the 3 different biorhythms on the current pylab figure
    """
    pylab.plot_date(
        person.dates, 
        map(physical, person.days), 
        label="%s's physical"%person.name, 
        linestyle=person.linestyle,
        fmt='g%s'%person.fmt
    )
    pylab.plot_date(
        person.dates,
        map(emotional,person.days),
        label="%s's emotional"%person.name,
        linestyle=person.linestyle,
        fmt='r%s'%person.fmt
    )
    pylab.plot_date(
        person.dates,
        map(intellectual, person.days),
        label="%s's intellectual"%person.name,
        linestyle=person.linestyle,
        fmt='b%s'%person.fmt
    )


def showchart(graph_title='unknown'):
    """
    apply the title, legend and labels to the current pylab figure then
    display it to the user
    """
    pylab.title("%s's Biorhythms"% graph_title)
    pylab.xlabel('Dates')
    pylab.legend()
    pylab.grid(True)
    pylab.show()

def usage():
    """show command line help"""
    print """usage: 
    pyorhythms.py -n Name -b YYYYMMDD [[-n Name2 -b YYYYMMDD]...][-t 'Title']"""
    
if __name__ == '__main__':
    
    FMT_LIST = ['o', '^', 's', 'p', '1', '.']
    PEOPLE_COUNT = 0
    DAY_COUNT = 33
    TITLE = None
    NOM = None
    BIRTHDAY = None
    
    try:
        OPTS, ARGS = getopt.gnu_getopt(
                sys.argv[1:], 
                "t:n:b:", 
                ["title=", "name=", "birth="]
            )
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        print sys.argv
        sys.exit(2)
    for o, a in OPTS:
        #print o, '|', a
        if o in ['-t', '--title']:
            TITLE = a
        
        if o in ['-n', '--name']:
            NOM = a
            if not TITLE:
                TITLE = NOM
            else:
                TITLE += "/%s" % NOM
                
        if o in ['-b', '--birth']:
            #print time.strptime(a,"%Y%m%d")[0:3]
            # date expected as YYYYMMDD, use the strptime to make it to a 
            # datetime 9 element list, then just grab the first 3 use those 
            # three elements in the list as arguments to the date() function 
            # using the * operator
            BIRTHDAY = date(*time.strptime(a, "%Y%m%d")[0:3])
            if NOM:
                p1 = Person(
                            number_of_days=DAY_COUNT, 
                            name=NOM, 
                            birthday=BIRTHDAY, 
                        )
                p1.linestyle = '-'
                p1.fmt = FMT_LIST[PEOPLE_COUNT % len(FMT_LIST)]
                plot(p1)
                PEOPLE_COUNT += 1
                
    if BIRTHDAY:
        if NOM:
            showchart(TITLE)
